// 源自Inferno utils/6l/l.h和相关文件。
// https:
// None
// None
// None
// None
// None
// None
// None
// None
// None
// None
// 特此向任何获得副本的人免费授予许可
// 本软件和相关文档文件（“软件”）的
// 在软件中不受限制，包括但不限于权利
// 使用、复制、修改、合并、发布、分发、再许可和/或销售
// 软件的副本，并允许向其提供软件的人员
// 按照以下条件提供：
// None
// 上述版权声明和本许可声明应包含在
// 软件的所有副本或主要部分。
// None
// 本软件按“原样”提供，无任何形式的明示或明示担保
// 默示，包括但不限于适销性保证，
// 适用于特定目的和非侵权。在任何情况下
// 作者或版权持有人应承担任何索赔、损害或其他责任
// 无论是在合同诉讼、侵权诉讼或其他诉讼中，由以下原因引起的责任：，
// 与本软件有关或与本软件的使用或其他交易有关
// 软件。

package obj

import (
	"bufio"
	"cmd/internal/dwarf"
	"cmd/internal/goobj"
	"cmd/internal/objabi"
	"cmd/internal/src"
	"cmd/internal/sys"
	"fmt"
	"sync"
	"sync/atomic"
)

// Addr是指令的参数。
// 一般形式及其编码为：
// None
// sym±偏移量（SYMCIND）（reg）（索引*刻度）
// 地址处的内存引用&sym（symkind）+偏移量+reg+索引*刻度。
// 可以省略sym（SYMCIND）、±偏移、（reg）、（索引*刻度）和*刻度中的任何一个。
// 如果（reg）和*scale都被省略，则生成的表达式（索引）将解析为（reg）。
// 要强制解析为索引*scale，请写入（索引*1）。
// 编码：
// type=type\u MEM
// name=symkind（name\u AUTO，…）或0（name\u NONE）
// sym=sym
// 偏移量=±偏移量
// reg=reg（reg_u*）
// 索引=索引（REG_u*）
// 比例=比例（1,2,4,8）
// None
// $<mem>
// 上面定义的内存引用的有效地址<mem>。
// 编码：与内存引用相同，但type=type\u ADDR。
// None
// $<±整数值>
// 这是$<mem>的特例，其中仅存在±偏移量。
// 它有一个单独的类型，便于识别。
// 编码：
// 类型=类型常数
// 偏移量=±整数值
// None
// *<mem>
// 通过上面定义的内存引用<mem>进行间接引用。
// 仅在x86上用于CALL/JMP*sym（SB），它调用/跳转到函数
// 指针存储在数据字sym（SB）中，而不是名为sym（SB）的函数。
// 编码：与上述相同，但类型=类型。
// None
// $*$<mem>
// 不再使用。
// 在具有实际SB寄存器的计算机上，$*$<mem>强制
// 指令编码使用完整的32位常量，而不是
// 指某人。
// None
// $<浮点文字>
// 浮点常量值。
// 编码：
// 类型=类型
// val=浮点值
// None
// $<字符串文字，最多8个字符>
// 字符串文字值（用于数据指令的原始字节）。
// 编码：
// 类型=类型
// val=字符串
// None
// <注册名称>
// 任何寄存器：整数、浮点、控件、段等。
// 如果要查找特定的寄存器类型，必须检查类型和寄存器值范围。
// 编码：
// 类型=类型
// reg=reg（reg_u*）
// None
// x（个人电脑）
// 编码：
// 类型=类型\分支
// val=Prog*参考或ELSE offset=目标pc（分支优先）
// None
// $±x-±y
// 文本的最终参数，指定局部帧大小x和参数大小y。
// 在此形式中，x和y仅为整数文字，而不是任意表达式。
// 这避免了由于使用-作为分隔符而导致的解析歧义。
// 选项是可选的。
// 如果TEXT的最后一个参数省略了-±y，则编码仍应保持不变
// 使用类型_TEXTSIZE（而不是类型_CONST），u.argsize=ArgsSizeUnknown。
// 编码：
// 类型=类型\文本大小
// 偏移量=x
// val=int32（y）
// None
// reg<<shift，reg>>shift，reg->shift，reg@>shift
// 移位寄存器值，用于ARM和ARM64。
// 在这种形式中，reg必须是寄存器，shift可以是寄存器或整数常量。
// 编码：
// 类型=类型\u移位
// 武装：
// 偏移量=（reg&15）|移位类型<<5 |计数
// shifttype=0,1,2,3表示<<，>>，->，@>
// 对于寄存器移位计数，计数=（reg&15）<<8 | 1<<4；对于整数常量，计数=（n&31）<<7。
// 在ARM64上：
// 偏移量=（reg&31）<<16 |移位类型<<22 |（计数&63）<<10
// shifttype=0,1,2表示<，>>，->
// None
// （注册，注册）
// 目的地寄存器对。当用作指令的最后一个参数时，
// 此表明确说明两个寄存器都是目的地。
// 编码：
// 类型=类型
// reg=第一寄存器
// 偏移量=第二寄存器
// None
// [注册，注册，注册]
// ARM、ARM64、386/AMD64的寄存器列表。
// 编码：
// type=type\u REGLIST
// 武装：
// 偏移量=列表中寄存器的位掩码；R0是低位。
// 在ARM64上：
// 偏移量=寄存器计数（Q：大小）|排列（操作码）|第一个寄存器
// 在386/AMD64上：
// reg=范围低寄存器
// 偏移量=2个压缩寄存器+种类标记（请参阅x86.EncodeRegisterRange）
// None
// 雷格，雷格
// 手臂的注册对。
// 第2类
// None
// （注册号+注册号）
// PPC64的寄存器对。
// 编码：
// type=type\u MEM
// reg=第一寄存器
// 索引=第二寄存器
// 比例=1
// None
// 注册号[US]XT[BHWX]
// ARM64的寄存器扩展
// 编码：
// 类型=类型
// reg=reg_uus]XT[BHWX]+寄存器+移位量
// 抵销=（（注册号&31）<<16）|（外部类型<<13）|（金额<<10）
// None
// 注册号<T>
// ARM64 SIMD寄存器的寄存器排列
// e、 g.：V1.S4、V2.S2、V7.D2、V2.H4、V6.B16
// 编码：
// 类型=类型
// 注册=注册+注册+安排
// None
// 注册号[索引]
// ARM64的寄存器元素
// 编码：
// 类型=类型
// reg=reg_元素+寄存器+排列
// 索引=元素索引

type Addr struct {
	Reg    int16
	Index  int16
	Scale  int16 // 有时持有登记簿。
	Type   AddrType
	Name   AddrName
	Class  int8
	Offset int64
	Sym    *LSym

	// 参数值：
	// 对于类型_SCONST，字符串
	// 对于类型_FCONST，浮点数为64
	// 对于类型_分支，a*Prog（可选）
	// 对于类型_TEXTSIZE，一个int32（可选）
	Val interface{}
}

type AddrName int8

const (
	NAME_NONE AddrName = iota
	NAME_EXTERN
	NAME_STATIC
	NAME_AUTO
	NAME_PARAM
	// 提及name@GOT（SB）是指全局抵销中的分录
	// “名称”的表。
	NAME_GOTREF
	// 指示这是对TOC锚的引用。
	NAME_TOCREF
)

// go:生成stringer-type AddrType

type AddrType uint8

const (
	TYPE_NONE AddrType = iota
	TYPE_BRANCH
	TYPE_TEXTSIZE
	TYPE_MEM
	TYPE_CONST
	TYPE_FCONST
	TYPE_SCONST
	TYPE_REG
	TYPE_ADDR
	TYPE_SHIFT
	TYPE_REGREG
	TYPE_REGREG2
	TYPE_INDIR
	TYPE_REGLIST
)

func (a *Addr) Target() *Prog {
	if a.Type == TYPE_BRANCH && a.Val != nil {
		return a.Val.(*Prog)
	}
	return nil
}
func (a *Addr) SetTarget(t *Prog) {
	if a.Type != TYPE_BRANCH {
		panic("setting branch target when type is not TYPE_BRANCH")
	}
	a.Val = t
}

func (a *Addr) SetConst(v int64) {
	a.Sym = nil
	a.Type = TYPE_CONST
	a.Offset = v
}

// Prog描述一条机器指令。
// None
// 一般指示表格为：
// None
// （1） As.Scond From[，…RestArgs]，To
// （2） As.Scond From，Reg[，…RestArgs]，To，RegTo2
// None
// 其中As为操作码，其他为参数：
// From、Reg是源，To、RegTo2是目的地。
// RestArgs可以保存其他源和目标。
// 通常，并非所有的论点都存在。
// 例如，MOVL R1、R2仅使用As=MOVL、From=R1、To=R2进行编码。
// Scond字段保存系统（如arm）的附加条件位
// 具有广义条件执行的。
// （2） 表单的存在是为了与旧代码兼容，
// 避免在一次摆动中发生太多变化。
// （1） scheme足以表示任何类型的操作数组合。
// None
// 跳转指令使用To.Val字段指向目标*Prog，
// 它必须与跳转指令位于同一个链表中。
// None
// 给定功能的程序排列在通过链接字段链接的列表中。
// None
// 每个Prog都会在调试信息中记录到特定的源代码行，
// 由位置行（）指定。
// 每个Prog都有一个定义其上下文的Ctxt字段。
// 出于性能原因，程序通常是大容量分配、缓存和重用的；
// 应该始终使用这些大容量分配器，而不是新的（Prog）。
// None
// 尚未提及的其他字段供后端使用，应该
// 程序列表的创建者将其置零。
type Prog struct {
	Ctxt     *Link     // 链接器上下文
	Link     *Prog     // 链表中的下一个程序
	From     Addr      // 第一源操作数
	RestArgs []AddrPos // 可以打包任何不适合{Prog.From，Prog.To}的操作数
	To       Addr      // 目标操作数（第二个是下面的RegTo2）
	Pool     *Prog     // 恒定池入口，用于arm、arm64后端
	Forwd    *Prog     // 用于x86后端
	Rel      *Prog     // 对于x86，手臂后端
	Pc       int64     // 对于后端或汇编程序：虚拟或实际程序计数器，取决于阶段
	Pos      src.XPos  // 此指令的源位置
	Spadj    int32     // 指令对堆栈指针的影响（增量或减量）
	As       As        // 汇编操作码
	Reg      int16     // 第二源操作数
	RegTo2   int16     // 第二目标操作数
	Mark     uint16    // arch特定项的位掩码
	Optab    uint16    // 特定于arch的操作码索引
	Scond    uint8     // 描述指令后缀的位（例如ARM条件）
	Back     uint8     // 对于x86后端：向后分支状态
	Ft       uint8     // 对于x86后端：Prog.From的类型索引
	Tt       uint8     // 对于x86后端：Prog.To的类型索引
	Isize    uint8     // 对于x86后端：指令的大小（字节）
}

// Pos表示oprand是源还是目标。
type AddrPos struct {
	Addr
	Pos OperandPos
}

type OperandPos int8

const (
	Source OperandPos = iota
	Destination
)

// From3Type返回p.GetFrom3（）.Type，或在
// p、 GetFrom3（）返回nil。
// None
// 不推荐：原因与Prog.GetFrom3相同。
func (p *Prog) From3Type() AddrType {
	if p.RestArgs == nil {
		return TYPE_NONE
	}
	return p.RestArgs[0].Type
}

// GetFrom3返回第二个源操作数（第一个是Prog.From）。
// 它与Prog.From和Prog.To结合使用，形成公共3操作数
// 外壳更容易使用。
// None
// 仅当使用SetFrom3设置RestArgs时才应使用。
// None
// 不推荐：最好直接使用RestArgs或定义后端特定的getter。
// 用于简化到[]Addr的转换。
// 由于其脆弱性和缺乏保障，不鼓励使用。
func (p *Prog) GetFrom3() *Addr {
	if p.RestArgs == nil {
		return nil
	}
	return &p.RestArgs[0].Addr
}

// SetFrom3将[]Args{{{a，0}}分配给p.RestArgs。
// 与Prog.GetFrom3配合使用，可以帮助模拟Prog.From3。
// None
// 不推荐：原因与Prog.GetFrom3相同。
func (p *Prog) SetFrom3(a Addr) {
	p.RestArgs = []AddrPos{{a, Source}}
}

// SetFrom3Reg使用包含reg的寄存器Addr调用p.SetFrom3。
// None
// 不推荐：原因与Prog.GetFrom3相同。
func (p *Prog) SetFrom3Reg(reg int16) {
	p.SetFrom3(Addr{Type: TYPE_REG, Reg: reg})
}

// SetFrom3Const使用包含x的常量Addr调用p.SetFrom3。
// None
// 不推荐：原因与Prog.GetFrom3相同。
func (p *Prog) SetFrom3Const(off int64) {
	p.SetFrom3(Addr{Type: TYPE_CONST, Offset: off})
}

// 当第二个目的地被删除时，SetTo2将[]Args{{{a，1}}分配给p.RestArgs
// 操作数不适合prog.RegTo2。
func (p *Prog) SetTo2(a Addr) {
	p.RestArgs = []AddrPos{{a, Destination}}
}

// GetTo2返回第二个目标操作数。
func (p *Prog) GetTo2() *Addr {
	if p.RestArgs == nil {
		return nil
	}
	return &p.RestArgs[0].Addr
}

// SetRestArgs为p.RestArgs分配多个源操作数。
func (p *Prog) SetRestArgs(args []Addr) {
	for i := range args {
		p.RestArgs = append(p.RestArgs, AddrPos{args[i], Source})
	}
}

// As表示汇编程序操作码。
// 有一些便携式操作码，在包obj中声明，
// 这是所有体系结构所共有的。
// 然而，大多数操作码都是特定于arch的
// 并在各自体系结构的子包中声明。
type As int16

// 这些是便携式操作码。
const (
	AXXX As = iota
	ACALL
	ADUFFCOPY
	ADUFFZERO
	AEND
	AFUNCDATA
	AJMP
	ANOP
	APCALIGN
	APCDATA
	ARET
	AGETCALLERPC
	ATEXT
	AUNDEF
	A_ARCHSPECIFIC
)

// 每个体系结构分配一个不同的操作码值子空间
// 用于声明其特定于arch的操作码。
// 在这个子空间中，第一个特定于arch的操作码应该是
// 在偏移量A_处。
// None
// 子空间按二的幂排列，因此可以屏蔽操作码
// 使用AMask并用作紧凑阵列索引。
const (
	ABase386 = (1 + iota) << 11
	ABaseARM
	ABaseAMD64
	ABasePPC64
	ABaseARM64
	ABaseMIPS
	ABaseRISCV
	ABaseS390X
	ABaseWasm

	AllowedOpCodes = 1 << 11            // 任何给定体系结构可用的操作码数。
	AMask          = AllowedOpCodes - 1 // 并使用该操作码作为数组索引。
)

// LSym是一种写入对象文件的符号。
// 它表示平面pkg+“+”名称空间中的Go符号。
type LSym struct {
	Name string
	Type objabi.SymKind
	Attribute

	Size   int64
	Gotype *LSym
	P      []byte
	R      []Reloc

	Extra *interface{} // *FuncInfo或*FileInfo（如果存在）

	Pkg    string
	PkgIdx int32
	SymIdx int32
}

// FuncInfo包含STEXT符号的额外字段。
type FuncInfo struct {
	Args     int32
	Locals   int32
	Align    int32
	FuncID   objabi.FuncID
	FuncFlag objabi.FuncFlag
	Text     *Prog
	Autot    map[*LSym]struct{}
	Pcln     Pcln
	InlMarks []InlMark
	spills   []RegSpill

	dwarfInfoSym       *LSym
	dwarfLocSym        *LSym
	dwarfRangesSym     *LSym
	dwarfAbsFnSym      *LSym
	dwarfDebugLinesSym *LSym

	GCArgs             *LSym
	GCLocals           *LSym
	StackObjects       *LSym
	OpenCodedDeferInfo *LSym
	ArgInfo            *LSym // 回溯的参数信息

	FuncInfoSym *LSym
}

// NewFuncInfo为LSym分配并返回FuncInfo。
func (s *LSym) NewFuncInfo() *FuncInfo {
	if s.Extra != nil {
		panic(fmt.Sprintf("invalid use of LSym - NewFuncInfo with Extra of type %T", *s.Extra))
	}
	f := new(FuncInfo)
	s.Extra = new(interface{})
	*s.Extra = f
	return f
}

// Func返回与s关联的*FuncInfo，否则返回nil。
func (s *LSym) Func() *FuncInfo {
	if s.Extra == nil {
		return nil
	}
	f, _ := (*s.Extra).(*FuncInfo)
	return f
}

// FileInfo包含由文件支持的SDATA符号的额外字段。
// （如果LSym.Extra是*FileInfo，那么LSym.P==nil。）
type FileInfo struct {
	Name string // 要读入对象文件的文件名
	Size int64  // 文件长度
}

// NewFileInfo为LSym分配并返回FileInfo。
func (s *LSym) NewFileInfo() *FileInfo {
	if s.Extra != nil {
		panic(fmt.Sprintf("invalid use of LSym - NewFileInfo with Extra of type %T", *s.Extra))
	}
	f := new(FileInfo)
	s.Extra = new(interface{})
	*s.Extra = f
	return f
}

// File返回与s关联的*FileInfo，否则返回nil。
func (s *LSym) File() *FileInfo {
	if s.Extra == nil {
		return nil
	}
	f, _ := (*s.Extra).(*FileInfo)
	return f
}

type InlMark struct {
	// 从内联正文中的指令解除时，标记
	// 我们应该去哪里放松一下。
	// id记录内联主体的全局内联id。
	// p记录一条指令在父（内联）帧中的位置。
	p  *Prog
	id int32
}

// 将p标记为在以下情况下设置为pc的指令：
// “展开”内联全局帧id。通常应该是
// 带文件的指令：调用站点上的行，并发生
// 就在内联函数的主体之前。
func (fi *FuncInfo) AddInlMark(p *Prog, id int32) {
	fi.InlMarks = append(fi.InlMarks, InlMark{p: p, id: id})
}

// AddSpill将溢出记录附加到FuncInfo fi的列表中
func (fi *FuncInfo) AddSpill(s RegSpill) {
	fi.spills = append(fi.spills, s)
}

// 记录自动变量的类型符号，以便链接器
// 发出该类型的DWARF类型信息。
func (fi *FuncInfo) RecordAutoType(gotype *LSym) {
	if fi.Autot == nil {
		fi.Autot = make(map[*LSym]struct{})
	}
	fi.Autot[gotype] = struct{}{}
}

// go：生成stringer类型的ABI

// ABI是文本符号的调用约定。
type ABI uint8

const (
	// ABI0是基于堆栈的稳定ABI。重要的是
	// 该值为“0”：我们无法区分
	// 汇编代码中对数据和ABI0文本符号的引用，
	// 因此，这并不区分没有符号的符号
	// ABI和带有ABI0的文本符号。
	ABI0 ABI = iota

	// ABIInternal是在Go之间可能发生变化的内部ABI
	// 版本。所有Go功能都使用内部ABI和
	// 编译器为与其他对象的调用和来自其他对象的调用生成包装器
	// 艾比斯。
	ABIInternal

	ABICount
)

// ParseABI将“abistr”中的字符串表示形式转换为
// 相应的ABI值。如果
// 识别abi字符串，否则为FALSE。
func ParseABI(abistr string) (ABI, bool) {
	switch abistr {
	default:
		return ABI0, false
	case "ABI0":
		return ABI0, true
	case "ABIInternal":
		return ABIInternal, true
	}
}

// ABISet是ABI值的位集。
type ABISet uint8

const (
	// ABISetCallable是任何函数都可以调用的所有ABI的集合
	// 可能使用调用。
	ABISetCallable ABISet = (1 << ABI0) | (1 << ABIInternal)
)

// 确保ABISet足够大以容纳所有ABI。
var _ ABISet = 1 << (ABICount - 1)

func ABISetOf(abi ABI) ABISet {
	return 1 << abi
}

func (a *ABISet) Set(abi ABI, value bool) {
	if value {
		*a |= 1 << abi
	} else {
		*a &^= 1 << abi
	}
}

func (a *ABISet) Get(abi ABI) bool {
	return (*a>>abi)&1 != 0
}

func (a ABISet) String() string {
	s := "{"
	for i := ABI(0); a != 0; i++ {
		if a&(1<<i) != 0 {
			if s != "{" {
				s += ","
			}
			s += i.String()
			a &^= 1 << i
		}
	}
	return s + "}"
}

// 属性是一组符号属性。
type Attribute uint32

const (
	AttrDuplicateOK Attribute = 1 << iota
	AttrCFunc
	AttrNoSplit
	AttrLeaf
	AttrWrapper
	AttrNeedCtxt
	AttrNoFrame
	AttrOnList
	AttrStatic

	// MakeTypelink意味着该类型在typelink表中应该有一个条目。
	AttrMakeTypelink

	// ReflectMethod表示函数可以调用reflect.Type.Method或
	// reflect.Type.MethodByName。匹配不精确（如reflect.Type
	// 可以通过自定义接口使用），因此可以使用ReflectMethod
	// 在某些情况下，当未调用反射包时设置。
	// None
	// 链接器用于确定可以修剪哪些方法。
	AttrReflectMethod

	// 本地意味着即使在编译Go代码以引用Go时，也要将符号设置为本地
	// 其他共享库中的符号，如此模式中的符号是全局的
	// 违约“本地”在这里是指动态链接器的意义，即不是
	// 在包含其属性的模块（共享库或可执行文件）外部可见
	// 释义（不编译以支持Go共享库时，所有符号均为
	// 从这个意义上讲是本地的，除非有cgo_导出指令）。
	AttrLocal

	// 功能符号；指示指定的函数是
	// 编译期间内联的目标
	AttrWasInlined

	// 索引表示此符号已分配索引（使用
	// 新对象文件格式）。
	AttrIndexed

	// 仅适用于类型描述符符号，UsedInFace表示此类型为
	// 转换为接口。
	// None
	// 链接器用于确定可以修剪哪些方法。
	AttrUsedInIface

	// ContentAddressable表示这是一个内容可寻址符号。
	AttrContentAddressable

	// ABI包装器是为编译器生成的文本符号设置的
	// 在ABI0和ABI0内部调用约定之间转换。
	AttrABIWrapper

	// attrABIBase是ABI编码的值
	// 属性这必须是最后一次；这之后的所有位都是
	// 假设为ABI值。
	// None
	// 必须是最后一位，因为高于该位的所有位都构成ABI。
	attrABIBase
)

func (a *Attribute) load() Attribute { return Attribute(atomic.LoadUint32((*uint32)(a))) }

func (a *Attribute) DuplicateOK() bool        { return a.load()&AttrDuplicateOK != 0 }
func (a *Attribute) MakeTypelink() bool       { return a.load()&AttrMakeTypelink != 0 }
func (a *Attribute) CFunc() bool              { return a.load()&AttrCFunc != 0 }
func (a *Attribute) NoSplit() bool            { return a.load()&AttrNoSplit != 0 }
func (a *Attribute) Leaf() bool               { return a.load()&AttrLeaf != 0 }
func (a *Attribute) OnList() bool             { return a.load()&AttrOnList != 0 }
func (a *Attribute) ReflectMethod() bool      { return a.load()&AttrReflectMethod != 0 }
func (a *Attribute) Local() bool              { return a.load()&AttrLocal != 0 }
func (a *Attribute) Wrapper() bool            { return a.load()&AttrWrapper != 0 }
func (a *Attribute) NeedCtxt() bool           { return a.load()&AttrNeedCtxt != 0 }
func (a *Attribute) NoFrame() bool            { return a.load()&AttrNoFrame != 0 }
func (a *Attribute) Static() bool             { return a.load()&AttrStatic != 0 }
func (a *Attribute) WasInlined() bool         { return a.load()&AttrWasInlined != 0 }
func (a *Attribute) Indexed() bool            { return a.load()&AttrIndexed != 0 }
func (a *Attribute) UsedInIface() bool        { return a.load()&AttrUsedInIface != 0 }
func (a *Attribute) ContentAddressable() bool { return a.load()&AttrContentAddressable != 0 }
func (a *Attribute) ABIWrapper() bool         { return a.load()&AttrABIWrapper != 0 }

func (a *Attribute) Set(flag Attribute, value bool) {
	for {
		v0 := a.load()
		v := v0
		if value {
			v |= flag
		} else {
			v &^= flag
		}
		if atomic.CompareAndSwapUint32((*uint32)(a), uint32(v0), uint32(v)) {
			break
		}
	}
}

func (a *Attribute) ABI() ABI { return ABI(a.load() / attrABIBase) }
func (a *Attribute) SetABI(abi ABI) {
	const mask = 1 // 现在只有一个ABI位。
	for {
		v0 := a.load()
		v := (v0 &^ (mask * attrABIBase)) | Attribute(abi)*attrABIBase
		if atomic.CompareAndSwapUint32((*uint32)(a), uint32(v0), uint32(v)) {
			break
		}
	}
}

var textAttrStrings = [...]struct {
	bit Attribute
	s   string
}{
	{bit: AttrDuplicateOK, s: "DUPOK"},
	{bit: AttrMakeTypelink, s: ""},
	{bit: AttrCFunc, s: "CFUNC"},
	{bit: AttrNoSplit, s: "NOSPLIT"},
	{bit: AttrLeaf, s: "LEAF"},
	{bit: AttrOnList, s: ""},
	{bit: AttrReflectMethod, s: "REFLECTMETHOD"},
	{bit: AttrLocal, s: "LOCAL"},
	{bit: AttrWrapper, s: "WRAPPER"},
	{bit: AttrNeedCtxt, s: "NEEDCTXT"},
	{bit: AttrNoFrame, s: "NOFRAME"},
	{bit: AttrStatic, s: "STATIC"},
	{bit: AttrWasInlined, s: ""},
	{bit: AttrIndexed, s: ""},
	{bit: AttrContentAddressable, s: ""},
	{bit: AttrABIWrapper, s: "ABIWRAPPER"},
}

// 字符串格式用于作为文本程序的一部分在中打印。
func (a Attribute) String() string {
	var s string
	for _, x := range textAttrStrings {
		if a&x.bit != 0 {
			if x.s != "" {
				s += x.s + "|"
			}
			a &^= x.bit
		}
	}
	switch a.ABI() {
	case ABI0:
	case ABIInternal:
		s += "ABIInternal|"
		a.SetABI(0) // 清除ABI，这样我们就不会在下面打印。
	}
	if a != 0 {
		s += fmt.Sprintf("UnknownAttribute(%d)|", a)
	}
	// 如果有尾随|，切掉尾随。
	if len(s) > 0 {
		s = s[:len(s)-1]
	}
	return s
}

// TextAttrString格式化符号属性，以便作为文本程序的一部分在中打印。
func (s *LSym) TextAttrString() string {
	attr := s.Attribute.String()
	if s.Func().FuncFlag&objabi.FuncFlag_TOPFRAME != 0 {
		if attr != "" {
			attr += "|"
		}
		attr += "TOPFRAME"
	}
	return attr
}

func (s *LSym) String() string {
	return s.Name
}

// 编译器需要*LSym才能分配给cmd/compile/internal/ssa.Sym。
func (*LSym) CanBeAnSSASym() {}
func (*LSym) CanBeAnSSAAux() {}

type Pcln struct {
	// pcln的辅助符号
	Pcsp        *LSym
	Pcfile      *LSym
	Pcline      *LSym
	Pcinline    *LSym
	Pcdata      []*LSym
	Funcdata    []*LSym
	Funcdataoff []int64
	UsedFiles   map[goobj.CUFileIndex]struct{} // 生成pcfile时使用的文件索引
	InlTree     InlTree                        // 从全局树中提取的每个函数内联树
}

type Reloc struct {
	Off  int32
	Siz  uint8
	Type objabi.RelocType
	Add  int64
	Sym  *LSym
}

type Auto struct {
	Asym    *LSym
	Aoffset int32
	Name    AddrName
	Gotype  *LSym
}

// RegSpill为寄存器驻留参数提供溢出/填充信息
// 对一个函数进行修改。这些需要在safepoint/stackgrowth案例中溢出/填充。
// 填充/溢出时，偏移必须根据体系结构进行调整
// 调整调用指令中出现的硬件SP。例如，对于AMD64，
// 偏移量为+8，因为返回地址被推送。
type RegSpill struct {
	Addr           Addr
	Reg            int16
	Spill, Unspill As
}

// Link保存从编译器编写目标代码的上下文
// 作为链接器输入或将该输入读入链接器。
type Link struct {
	Headtype           objabi.HeadType
	Arch               *LinkArch
	Debugasm           int
	Debugvlog          bool
	Debugpcln          string
	Flag_shared        bool
	Flag_dynlink       bool
	Flag_linkshared    bool
	Flag_optimize      bool
	Flag_locationlists bool
	Retpoline          bool // 发出对间接jmp/call的retpoline存根的使用
	Bso                *bufio.Writer
	Pathname           string
	Pkgpath            string           // 当前包的导入路径为“”（如果未知）
	hashmu             sync.Mutex       // 保护哈希，funchash
	hash               map[string]*LSym // 名称->符号映射
	funchash           map[string]*LSym // 名称->内部符号的符号映射
	statichash         map[string]*LSym // 名称->静态符号的符号映射
	PosTable           src.PosTable
	InlTree            InlTree // gc/inl.go使用的全局内联树
	DwFixups           *DwarfFixupTable
	Imports            []goobj.ImportedPkg
	DiagFunc           func(string, ...interface{})
	DiagFlush          func()
	DebugInfo          func(fn *LSym, info *LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCalls) // 如果非nil，curfn是*gc.Node
	GenAbstractFunc    func(fn *LSym)
	Errors             int

	InParallel    bool // 并行后端阶段生效
	UseBASEntries bool // 在位置列表和PC范围中使用基址选择项
	IsAsm         bool // 是源汇编语言，可能包含令人惊讶的习惯用法（例如，调用表）

	// 写入对象的状态
	Text []*LSym
	Data []*LSym

	// AbiAlias是文本符号，应为所有
	// 艾比斯。这些符号只能引用，不能定义
	// 通过此对象，因为需要别名可能出现在
	// 与定义不同的对象。因此,
	// 符号定义中不能包含信息。
	// None
	// TODO（奥斯汀）：一旦ABI关闭，就用ABI包装器替换它
	// 实际上是分歧。
	ABIAliases []*LSym

	// 常量符号（例如$i64.*）是延迟创建的数据符号
	// 在并行阶段。为了确保确定的顺序，我们
	// 将它们添加到一个单独的列表中，在末尾排序，然后将其追加
	// 数据。
	constSyms []*LSym

	// pkgIdx将包路径映射到索引。该索引用于
	// 对象文件中的符号引用。
	pkgIdx map[string]int32

	defs         []*LSym // 当前包中已定义符号的列表
	hashed64defs []*LSym // 已定义的短（64位或更少）散列（内容可寻址）符号列表
	hasheddefs   []*LSym // 定义的哈希（内容可寻址）符号列表
	nonpkgdefs   []*LSym // 已定义的非软件包符号列表
	nonpkgrefs   []*LSym // 参考的非包装符号列表

	Fingerprint goobj.FingerprintType // 符号索引的指纹，以捕获索引不匹配
}

func (ctxt *Link) Diag(format string, args ...interface{}) {
	ctxt.Errors++
	ctxt.DiagFunc(format, args...)
}

func (ctxt *Link) Logf(format string, args ...interface{}) {
	fmt.Fprintf(ctxt.Bso, format, args...)
	ctxt.Bso.Flush()
}

// SpillRegisterArgs发出将寄存器args溢出到任意位置的代码
// 泄漏记录指定的位置。
func (fi *FuncInfo) SpillRegisterArgs(last *Prog, pa ProgAlloc) *Prog {
	// 溢出寄存器args。
	for _, ra := range fi.spills {
		spill := Appendp(last, pa)
		spill.As = ra.Spill
		spill.From.Type = TYPE_REG
		spill.From.Reg = ra.Reg
		spill.To = ra.Addr
		last = spill
	}
	return last
}

// UnspillRegisterArgs发出代码，从任何位置还原寄存器参数
// 泄漏记录指定的位置。
func (fi *FuncInfo) UnspillRegisterArgs(last *Prog, pa ProgAlloc) *Prog {
	// 取消填充任何溢出的寄存器参数
	for _, ra := range fi.spills {
		unspill := Appendp(last, pa)
		unspill.As = ra.Unspill
		unspill.From = ra.Addr
		unspill.To.Type = TYPE_REG
		unspill.To.Reg = ra.Reg
		last = unspill
	}
	return last
}

// 从硬件堆栈指针到本地
// 堆栈上的变量。使用链接寄存器的体系结构可以保存其值
// 在函数序言中的堆栈上，因此始终在
// 硬件堆栈指针和局部变量区域。
func (ctxt *Link) FixedFrameSize() int64 {
	switch ctxt.Arch.Family {
	case sys.AMD64, sys.I386, sys.Wasm:
		return 0
	case sys.PPC64:
		// ppc64le上的PIC代码需要32字节的堆栈，并且更容易实现
		// 只要在ppc64x上始终使用那么多堆栈即可。
		return int64(4 * ctxt.Arch.PtrSize)
	default:
		return int64(ctxt.Arch.PtrSize)
	}
}

// LinkArch是单个体系结构的定义。
type LinkArch struct {
	*sys.Arch
	Init           func(*Link)
	ErrorCheck     func(*Link, *LSym)
	Preprocess     func(*Link, *LSym, ProgAlloc)
	Assemble       func(*Link, *LSym, ProgAlloc)
	Progedit       func(*Link, *Prog, ProgAlloc)
	UnaryDst       map[As]bool // 指令接受一个操作数，即目标。
	DWARFRegisters map[int16]int16
}
